package main

import (
	"database/sql"
	"os"
)

/*
#谨慎使用
init 函数用于初始化应用的状态，但不可以被直接调用。init函数只会被调用一次，也就是只执行一次。
当一个 package 被初始化的时候，所有的定义的常量和变量先初始化，再执行 init 函数。(const > var > init)
如果依赖了其他 package 的代码，则先初始化依赖方的（const > var > init）。这个顺序再多层依赖的时候可以继续推广。
而同一个 package 中的 init 函数按照字典序调用。但不要依赖这种顺序，这没有意义。
*/
func main() {

}

var db *sql.DB

// 错误示例
/**
缺点：
1. init 函数中错误处理受限，一旦出错会 panic，不允许这个 package 的调用者手动处理错误逻辑。
2. 为这个文件写单测的时候，即使测试其他函数，init 函数也会被调用，使得单测流程复杂化。
3. 将数据库连接赋值给全局变量会导致问题：
	a. 任何函数可以对其进行修改。
	b. 依赖了全局变量，单测运行会更复杂，因为它变得不再隔离。
修正方案：
使用普通函数createClient来处理

所有我们应该慎用init函数，当在一些特殊场景使用，如定义静态配置
*/
func init() {
	dataSourceName := os.Getenv("MYSQL_DATA_SOURCE_NAME")
	d, err := sql.Open("mysql", dataSourceName)
	if err != nil {
		panic(err)
	}
	err = d.Ping()
	if err != nil {
		panic(err)
	}
	db = d
}

func createClient(dsn string) (*sql.DB, error) {
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, err
	}
	if err = db.Ping(); err != nil {
		return nil, err
	}
	return db, nil
}
